namespace Fsharp.wpf

module Core =
    open System
    open System.ComponentModel
    open System.Windows.Input
    open Microsoft.FSharp.Quotations
    open Microsoft.FSharp.Quotations.Patterns
    open System.Diagnostics
    open System.Runtime.CompilerServices
    open System.Runtime.InteropServices
    /// 统一的ViewModel基类（合并基础版和增强版）
    type ViewModelBase() =
        let propertyChanged = Event<_, _>()
        let errorsChanged = Event<_, _>()
        let validationErrors = Collections.Generic.Dictionary<string, string list>()

        let getPropertyName (expr: Expr) =
            match expr with
            | PropertyGet(_, prop, _) -> prop.Name
            | _ -> ""

        interface INotifyPropertyChanged with
            [<CLIEvent>]
            member _.PropertyChanged = propertyChanged.Publish

        interface INotifyDataErrorInfo with
            [<CLIEvent>]
            member _.ErrorsChanged = errorsChanged.Publish
            member _.HasErrors = validationErrors.Count > 0
            member _.GetErrors(propertyName: string) =
                validationErrors.TryGetValue(propertyName) 
                |> function 
                   | true, errors -> errors :> Collections.IEnumerable 
                   | false, _ -> [||] :> Collections.IEnumerable

        // member this.OnPropertyChanged([<ReflectedDefinition>] expr: Expr) =
        //     propertyChanged.Trigger(this, PropertyChangedEventArgs(getPropertyName expr))

        member this.OnPropertyChanged([<CallerMemberName; Optional; DefaultParameterValue("")>] propertyName: string) =
            propertyChanged.Trigger(this, PropertyChangedEventArgs(propertyName))

        member this.AddError(propertyName: string, error: string) =
            validationErrors.[propertyName] <- [error]
            errorsChanged.Trigger(this, DataErrorsChangedEventArgs(propertyName))

        member this.ClearErrors(propertyName: string) =
            validationErrors.Remove(propertyName) |> ignore
            errorsChanged.Trigger(this, DataErrorsChangedEventArgs(propertyName))

    /// 统一的命令实现（合并基础版、异步版）
    type RelayCommand(execute: obj -> unit, canExecute: obj -> bool) =
        let canExecuteChanged = Event<_, _>()

        interface ICommand with
            [<CLIEvent>]
            member _.CanExecuteChanged = canExecuteChanged.Publish
            member _.CanExecute(parameter) = canExecute parameter
            member _.Execute(parameter) = execute parameter

        member this.RaiseCanExecuteChanged() =
            canExecuteChanged.Trigger(this, EventArgs())

    type AsyncRelayCommand(execute: obj -> Async<unit>, canExecute: obj -> bool) as this =
        let mutable isExecuting = false
        let canExecuteChanged = Event<_, _>()

        member val IsExecuting = false with get, set

        interface ICommand with
            [<CLIEvent>]
            member _.CanExecuteChanged = canExecuteChanged.Publish
            member _.CanExecute(parameter) = not this.IsExecuting && canExecute parameter
            member _.Execute(parameter) = 
                this.IsExecuting <- true
                async {
                    try do! execute parameter
                    finally this.IsExecuting <- false
                } |> Async.StartImmediate

    /// 统一的命令创建模块
    module Commands =
        let create execute canExecute = RelayCommand(execute, canExecute) :> ICommand
        let createSimple execute = RelayCommand(execute, fun _ -> true) :> ICommand
        let createAsync execute canExecute = AsyncRelayCommand(execute, canExecute) :> ICommand
